package peerToPeerFacebook.proj;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class RPCPacketBase implements Serializable
{
	/** This field is used only for debugging. As our packets are serialized it is difficult to figure out what they
	 * mean. So before we encode we will set this field to some readable packet representation and then encode. Thus, we
	 * would be able to read what this packet means. While this increase packet size, this is not big deal. It can
	 * easily be disabled.
	 */
	protected String description = null;
	private static final long serialVersionUID = -9070793378602968022L;

	public enum Action { Invoke, Return }

	public long sequenceID = -1;
	public Action action;
	
	public RPCPacketBase(long sequenceID, Action action)
	{
		this.sequenceID = sequenceID;
		this.action = action;
	}

	public static RPCPacketBase decode(byte[] msg) throws RPCException
	{
		try 
		{
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(msg));
			return (RPCPacketBase)in.readObject();
		}
		catch (IOException e) 
		{
			throw new RPCException("Failed to decode incoming packet: ", e);
		}
		catch (ClassNotFoundException e) 
		{
			throw new RPCException("Failed to decode incoming packet: ", e);
		}
	}

	/** Encodes the packet so that it is ready to pass over network.
	 * This will also add debugging information so that packet meaning can be seen from the encoded packet.
	 * 
	 * @return Encoded byte stream.
	 */
	public byte[] encode() throws RPCError
	{
		// Add some nice description of packet on the wire.
		// it is alot nicer with the newlines
		description = "\n --> " + this.toString() + "\n";

		try {
			ByteArrayOutputStream bits = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(bits);
			out.writeObject(this);
			out.flush();
			return bits.toByteArray();
		} catch(IOException e) {
			throw new RPCError("Failed to encode packet: " + e);
		}
	}

	/** Builds representation of the packet.
	 */
	@Override
	public String toString()
	{
		return "SeqID: " + sequenceID + ", action: " + action;
	}

	/** Returns staring definition of the set of parameters. Used in converting to string.
	 * @param objects Objects whose definition should be stringified.
	 * @return String definition of the set of the parameters.
	 */
	protected StringBuilder getStringDefinition(Serializable[] objects) {
		StringBuilder paramNames = new StringBuilder();
		for (int i = 0; i < objects.length; i++)
		{
			if (objects[i] != null)
			{
				paramNames.append(objects[i].getClass().getName());
				paramNames.append("(");
				paramNames.append(objects[i].toString());
				paramNames.append(")");
			}
			else
				paramNames.append("null");
			
			if (i + 1 < objects.length)
				paramNames.append(", ");
		}
		return paramNames;
	}

}
